#include "RHISampler.h"
#include "../../Core/CoreGlobals.h"
#include "../../Utils/LogUtil.h"
#include "../../Templates/NewObject.h"
#include "../RenderState.h"
#include "../../Misc/Crc.h"
#include "../../Game/Process/RenderProcess.h"

RHISampler::BuilderDetails::BuilderDetails()
{
	samplerCreateInfo = {};
	samplerCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
	samplerCreateInfo.magFilter = VK_FILTER_LINEAR;
	samplerCreateInfo.minFilter = VK_FILTER_LINEAR;
	samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
	samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
	samplerCreateInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
	samplerCreateInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
	samplerCreateInfo.mipLodBias = 0;
	samplerCreateInfo.compareEnable = VK_FALSE;
	samplerCreateInfo.compareOp = VK_COMPARE_OP_ALWAYS;
	samplerCreateInfo.minLod = 0;
	samplerCreateInfo.maxLod = 1;
	samplerCreateInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
	samplerCreateInfo.unnormalizedCoordinates = VK_FALSE;
	samplerCreateInfo.anisotropyEnable = GVulkanRHI->GetDeviceFeatures().samplerAnisotropy ? VK_TRUE : VK_FALSE;
	samplerCreateInfo.maxAnisotropy = GVulkanRHI->GetDeviceFeatures().samplerAnisotropy ? GVulkanRHI->GetDeviceProperties().limits.maxSamplerAnisotropy : 1.f;
}

RHISampler::Builder::Builder() noexcept = default;
RHISampler::Builder::~Builder() noexcept = default;
RHISampler::Builder::Builder(RHISampler::Builder const& rhs) noexcept = default;
RHISampler::Builder::Builder(RHISampler::Builder && rhs) noexcept = default;
RHISampler::Builder& RHISampler::Builder::operator=(RHISampler::Builder const& rhs) noexcept = default;
RHISampler::Builder& RHISampler::Builder::operator=(RHISampler::Builder && rhs) noexcept = default;

RHISampler::Builder& RHISampler::Builder::SetMagFilter(VkFilter magFilter) noexcept
{
	mImpl->samplerCreateInfo.magFilter = magFilter;
	return *this;
}

RHISampler::Builder& RHISampler::Builder::SetMinFilter(VkFilter minFilter) noexcept
{
	mImpl->samplerCreateInfo.minFilter = minFilter;
	return *this;
}

RHISampler::Builder& RHISampler::Builder::SetMipmapMode(VkSamplerMipmapMode mipmapMode) noexcept
{
	mImpl->samplerCreateInfo.mipmapMode = mipmapMode;
	return *this;
}

RHISampler::Builder& RHISampler::Builder::SetAddressMode(VkSamplerAddressMode addressMode) noexcept
{
	mImpl->samplerCreateInfo.addressModeU = addressMode;
	mImpl->samplerCreateInfo.addressModeV = addressMode;
	mImpl->samplerCreateInfo.addressModeW = addressMode;
	return *this;
}

RHISampler::Builder& RHISampler::Builder::SetAddressModeU(VkSamplerAddressMode addressModeU) noexcept
{
	mImpl->samplerCreateInfo.addressModeU = addressModeU;
	return *this;
}

RHISampler::Builder& RHISampler::Builder::SetAddressModeV(VkSamplerAddressMode addressModeV) noexcept
{
	mImpl->samplerCreateInfo.addressModeV = addressModeV;
	return *this;
}

RHISampler::Builder& RHISampler::Builder::SetAddressModeW(VkSamplerAddressMode addressModeW) noexcept
{
	mImpl->samplerCreateInfo.addressModeW = addressModeW;
	return *this;
}



RHISampler::Builder& RHISampler::Builder::SetMipLodBias(float mipLodBias) noexcept
{
	mImpl->samplerCreateInfo.mipLodBias = mipLodBias;
	return *this;
}

RHISampler::Builder& RHISampler::Builder::SetAnisotropyEnable(VkBool32 anisotropyEnable) noexcept
{
	mImpl->samplerCreateInfo.anisotropyEnable = anisotropyEnable;
	return *this;
}

RHISampler::Builder& RHISampler::Builder::SetCompareEnable(VkBool32 compareEnable) noexcept
{
	mImpl->samplerCreateInfo.compareEnable = compareEnable;
	return *this;
}

RHISampler::Builder& RHISampler::Builder::SetCompareOp(VkCompareOp compareOp) noexcept
{
	mImpl->samplerCreateInfo.compareOp = compareOp;
	return *this;
}

RHISampler::Builder& RHISampler::Builder::SetMinLod(float minLod) noexcept
{
	mImpl->samplerCreateInfo.minLod = minLod;
	return *this;
}

RHISampler::Builder& RHISampler::Builder::SetMaxLod(float maxLod) noexcept
{
	mImpl->samplerCreateInfo.maxLod = maxLod;
	return *this;
}

RHISampler::Builder& RHISampler::Builder::SetBorderColor(VkBorderColor borderColor) noexcept
{
	mImpl->samplerCreateInfo.borderColor = borderColor;
	return *this;
}

RHISampler::Builder& RHISampler::Builder::SetUnnormalizedCoordinates(VkBool32 unnormalizedCoordinates) noexcept
{
	mImpl->samplerCreateInfo.unnormalizedCoordinates = unnormalizedCoordinates;
	return *this;
}

RHISampler::Builder& RHISampler::Builder::SetMaxAnisotropy(float maxAnisotropy) noexcept
{
	mImpl->samplerCreateInfo.maxAnisotropy = maxAnisotropy;
	return *this;
}

RefCountPtr<RHISampler> RHISampler::Builder::Build()
{
	assert(IsInRenderThread());
	const uint32 hash = GetHash();
	RefCountPtr<RHISampler> sampler = GRenderState->GetSamplerByHash(hash);
	if (!sampler.IsValid())
	{
		sampler = RefCountPtr<RHISampler>(NewObject<RHISampler>(*mImpl));
		GRenderState->RegisterSampler(hash, sampler);
	}
	return sampler;
}

RefCountPtr<RHISampler> RHISampler::Builder::Build(RenderProcess* process, const std::string& name)
{
	assert(IsInRenderThread());
	const uint32 hash = GetHash();
	RefCountPtr<RHISampler> sampler = GRenderState->GetSamplerByHash(hash);
	if (!sampler.IsValid())
	{
		sampler = RefCountPtr<RHISampler>(NewObject<RHISampler>(*mImpl));
		GRenderState->RegisterSampler(hash, sampler);
	}
	process->AddSampler(name, sampler.GetReference());
	return sampler;
}

void RHISampler::ReleaseResource()
{
	if (IsInRenderThread())
	{
		if (mProcess != nullptr)
		{
			mProcess->RemoveSampler(mName, this);
		}
		DeleteObject<RHISampler>(this);
	}
	else
	{
		AsyncTask(GRenderThreadId, [this]() {
			if (mProcess != nullptr)
			{
				mProcess->RemoveSampler(mName, this);
			}
			DeleteObject<RHISampler>(this);
			});
	}
}

uint32 RHISampler::Builder::GetHash() const
{
	return CRC32((uint8*)&mImpl->samplerCreateInfo, sizeof(VkSamplerCreateInfo));
}

RHISampler::~RHISampler()
{
	vkDestroySampler(GVulkanRHI->GetLogicalDevice(), mSampler, nullptr);
}

RHISampler::RHISampler(BuilderDetails & builderDetails)
{
	VK_CHECK_RESULT(vkCreateSampler(GVulkanRHI->GetLogicalDevice(), &builderDetails.samplerCreateInfo, nullptr, &mSampler));
}

VkSampler RHISampler::GetSampler() const
{
	return mSampler;
}

